﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using Tesla.Content;
using Tesla.Math;

namespace Tesla.Graphics {
    /// <summary>
    /// The Light class is a base class for all lighting objects used
    /// by the engine. Common properties such as ambient, diffuse,
    /// and specular color as well as attenuation properties are defined
    /// in this class, but may not be used by all subclasses.
    /// </summary>
    public abstract class Light : ISavable {
        private String _name;
        private Color _ambient;
        private Color _diffuse;
        private Color _specular;
        private float _constant;
        private float _linear;
        private float _quadratic;
        private bool _attenuate;
        private bool _enabled;

        /// <summary>
        /// Gets or sets the name of this light.
        /// </summary>
        public String Name {
            get {
                return _name;
            }
            set {
                _name = value;
            }
        }


        /// <summary>
        /// Gets or sets the ambient contribution of this light,
        /// which is the color that appears to be coming from everywhere.
        /// 
        /// Default: Color(.4f, .4f, .4f, 1.0f);
        /// </summary>
        public Color Ambient {
            get {
                return _ambient;
            }
            set {
                _ambient = value;
            }
        }

        /// <summary>
        /// Gets or sets the diffuse contribution of this light,
        /// which is the color directly from the light source.
        /// 
        /// Default: Color(1.0f, 1.0f, 1.0f, 1.0f)
        /// </summary>
        public Color Diffuse {
            get {
                return _diffuse;
            }
            set {
                _diffuse = value;
            }
        }

        /// <summary>
        /// Gets or sets the specular contribution of this light,
        /// which is the color reflected by an object depending on
        /// how shiny it is.
        /// 
        /// Default: Color(1.0f, 1.0f, 1.0f, 1.0f)
        /// </summary>
        public Color Specular {
            get {
                return _specular;
            }
            set {
                _specular = value;
            }
        }

        /// <summary>
        /// Gets or sets the attenuation constant, which is part
        /// of this formula:
        /// 
        /// 1 / (C + L*d + Q*d*d)
        /// 
        /// Where C,L, and Q are the attenuation values
        /// and d is the distance from the light source to
        /// the part of the object being lit.
        /// 
        /// Default: 1.0f
        /// </summary>
        public float Constant {
            get {
                return _constant;
            }
            set {
                _constant = value;
            }
        }

        /// <summary>
        /// Gets or sets the attenuation linear value, which is
        /// part of this formula:
        /// 
        /// 1 / (C + L*d + Q*d*d)
        /// 
        /// Where C,L, and Q are the attenuation values
        /// and d is the distance from the light source to
        /// the part of the object being lit.
        /// 
        /// Default: 0.0f
        /// </summary>
        public float Linear {
            get {
                return _linear;
            }
            set {
                _linear = value;
            }
        }

        /// <summary>
        /// Gets or sets the attenuation quadratic value,
        /// which is part of this formula:
        /// 
        /// 1 / (C + L*d + Q*d*d)
        /// 
        /// Where C,L, and Q are the attenuation values
        /// and d is the distance from the light source to
        /// the part of the object being lit.
        /// 
        /// Default: 0.0f
        /// </summary>
        public float Quadratic {
            get {
                return _quadratic;
            }
            set {
                _quadratic = value;
            }
        }

        /// <summary>
        /// Gets or sets if this light should attenuate. Note,
        /// some lights may ignore this (e.g. Directional lights
        /// should never attenuate)
        /// 
        /// Default: True
        /// </summary>
        public bool Attenuate {
            get {
                return _attenuate;
            }
            set {
                _attenuate = value;
            }
        }

        /// <summary>
        /// Gets or sets if this light should be enabled or not.
        /// 
        /// Default: True
        /// </summary>
        public bool IsEnabled {
            get {
                return _enabled;
            }
            set {
                _enabled = value;
            }
        }

        /// <summary>
        /// Gets the light type for this light.
        /// </summary>
        public abstract LightType LightType {
            get;
        }

        /// <summary>
        /// Base constructor that initializes the default
        /// light values.
        /// </summary>
        public Light() {
            _name = "Light";
            _ambient = new Color(0.4f, 0.4f, 0.4f, 1.0f);
            _diffuse = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            _specular = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            _constant = 1.0f;
            _linear = 0.0f;
            _quadratic = 0.0f;
            _attenuate = true;
            _enabled = true;
        }

        /// <summary>
        /// Base constructor that initializes the default light values
        /// and sets the specified name.
        /// </summary>
        /// <param name="name">Light's name</param>
        public Light(String name) {
            _name = name;
            _ambient = new Color(0.4f, 0.4f, 0.4f, 1.0f);
            _diffuse = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            _specular = new Color(1.0f, 1.0f, 1.0f, 1.0f);
            _constant = 1.0f;
            _linear = 0.0f;
            _quadratic = 0.0f;
            _attenuate = true;
            _enabled = true;
        }

        /// <summary>
        /// Serializes this Light.
        /// </summary>
        /// <param name="output">Output to be written to</param>
        public virtual void Write(ISavableWriter output) {
            output.Write("Name", _name);
            output.Write("Ambient", _ambient);
            output.Write("Diffuse", _diffuse);
            output.Write("Specular", _specular);
            output.Write("Constant", _constant);
            output.Write("Linear", _linear);
            output.Write("Quadratic", _quadratic);
            output.Write("Attenuate", _attenuate);
            output.Write("IsEnabled", _enabled);
        }

        /// <summary>
        /// Deserializes this Light.
        /// </summary>
        /// <param name="input">Input to read from</param>
        public virtual void Read(ISavableReader input) {
            _name = input.ReadString();
            _ambient = input.ReadColor();
            _diffuse = input.ReadColor();
            _specular = input.ReadColor();
            _constant = input.ReadSingle();
            _linear = input.ReadSingle();
            _quadratic = input.ReadSingle();
            _attenuate = input.ReadBoolean();
            _enabled = input.ReadBoolean();
        }
    }
}
